using UnityEngine; namespace FlatKit { public class Buoyancy : MonoBehaviour { [Tooltip("The object that contains a Water material.")] public Transform water; [Space] [Tooltip("Range of probing wave height for buoyancy rotation.")] public float size = 1f; [Tooltip("Max height of buoyancy going up and down.")] public float amplitude = 1f; [Space, Tooltip("Optionally provide a separate material to get the wave parameters.")] public Material overrideWaterMaterial; private Material _material; private float _speed; private float _amplitude; private float _frequency; private float _direction; private Vector3 _originalPosition; private Quaternion _originalRotation; private void Start() { var r = water.GetComponent(); Debug.Assert(r); _material = overrideWaterMaterial != null ? overrideWaterMaterial : r.sharedMaterial; Debug.Assert(_material); Debug.Assert(_material.HasProperty("_WaveSpeed")); _speed = _material.GetFloat("_WaveSpeed"); _amplitude = _material.GetFloat("_WaveAmplitude"); _frequency = _material.GetFloat("_WaveFrequency"); _direction = _material.GetFloat("_WaveDirection"); var t = transform; _originalPosition = t.position; _originalRotation = t.rotation; } private void Update() { var positionWS = transform.position; var positionOS = water.InverseTransformPoint(positionWS); positionWS.y = GetHeightOS(positionOS) + _originalPosition.y; transform.position = positionWS; var normal = GetNormalWS(positionOS); transform.rotation = Quaternion.FromToRotation(Vector3.up, normal) * _originalRotation; } Vector2 GradientNoiseDir(Vector2 p) { p = new Vector2(p.x % 289, p.y % 289); float x = (34 * p.x + 1) * p.x % 289 + p.y; x = (34 * x + 1) * x % 289; x = ((x / 41) % 1) * 2 - 1; return (new Vector2(x - Mathf.Floor(x + 0.5f), Mathf.Abs(x) - 0.5f)).normalized; } float GradientNoise(Vector2 p) { Vector2 ip = new Vector2(Mathf.Floor(p.x), Mathf.Floor(p.y)); Vector2 fp = new Vector2(p.x % 1, p.y % 1); float d00 = Vector3.Dot(GradientNoiseDir(ip), fp); float d01 = Vector3.Dot(GradientNoiseDir(ip + Vector2.up), fp - Vector2.up); float d10 = Vector3.Dot(GradientNoiseDir(ip + Vector2.right), fp - Vector2.right); float d11 = Vector3.Dot(GradientNoiseDir(ip + Vector2.one), fp - Vector2.one); fp = fp * fp * fp * (fp * (fp * 6f - Vector2.one * 15f) + Vector2.one * 10f); return Mathf.Lerp(Mathf.Lerp(d00, d01, fp.y), Mathf.Lerp(d10, d11, fp.y), fp.x); } private Vector3 GetNormalWS(Vector3 positionOS) { Vector3 b = positionOS + Vector3.forward * size; b.y = GetHeightOS(b); Vector3 c = positionOS + Vector3.right * size; c.y = GetHeightOS(b); Vector3 n = Vector3.Cross(b - positionOS, c - positionOS).normalized; return water.TransformDirection(n); } private float SineWave(Vector3 positionOS, float offset) { // Shader: // sin(offset + _Time.z * _WaveSpeed + (pos.x * sin(offset + _WaveDirection) + pos.z * // cos(offset + _WaveDirection)) * _WaveFrequency); float timez = Time.timeSinceLevelLoad * 2f; float s = Mathf.Sin(offset + timez * _speed + (positionOS.x * Mathf.Sin(offset + _direction) + positionOS.z * Mathf.Cos(offset + _direction)) * _frequency); if (_material.IsKeywordEnabled("_WAVEMODE_POINTY")) { s = 1.0f - Mathf.Abs(s); } return s * _amplitude; } private float GetHeightOS(Vector3 positionOS) { float y = SineWave(positionOS, 0.0f); if (_material.IsKeywordEnabled("_WAVEMODE_GRID")) { y *= SineWave(positionOS, 1.57f); } y *= amplitude; return y; } } }